
#include "tp_typedef.h"
#include "tp_main_loop.h"
#include "tp_source_primary.h"
#include "tp_globals.h"

typedef struct _TpMainLoop
{
	int running;
	int need_to_refresh;

	volatile int source_nr;
	TpSource* sources[TP_MAX_SOURCE];
    TpSource* primary_source;
}TpMainLoop;

static TpMainLoop g_main_loop = {0};

int tp_main_loop_add_source_2(TpSource* source)
{
	if (NULL == source) return TP_RET_BADARGS;

    if(g_main_loop.source_nr >= TP_MAX_SOURCE)
    {
        return TP_RET_FAIL;
    }

    g_main_loop.sources[g_main_loop.source_nr] = source;
    g_main_loop.source_nr++;

    if(g_main_loop.running)
    {
        g_main_loop.need_to_refresh = 1;
    }

	return TP_RET_OK;
}

static void tp_main_loop_remove_source_2(TpSource* source)
{
    int i, j;

    if(NULL == source)
    {
        return;
    }

    for(i = 0; i < g_main_loop.source_nr; ++i)
    {
        if(g_main_loop.sources[i] == source)
        {
            for(j = i; j< g_main_loop.source_nr; ++j)
            {
                g_main_loop.sources[j] = g_main_loop.sources[j+1];
            }
            g_main_loop.sources[g_main_loop.source_nr] = NULL;
            tp_source_destroy(source);

            g_main_loop.need_to_refresh = 1;
        }
    }

    return;
}

static TpRet tp_main_loop_on_event(void* user_data, TpEvent* event)
{
    if(user_data != &g_main_loop || NULL == event)
    {
        return TP_RET_BADARGS;
    }

    if(TP_EVENT_ADD_SOURCE != event->type || TP_EVENT_REMOVE_SOURCE != event->type)
    {
        return TP_RET_FAIL;
    }

    if(TP_EVENT_ADD_SOURCE == event->type)
    {
        tp_main_loop_add_source_2((TpSource*)event->u_event.source);
    }
    else if(TP_EVENT_REMOVE_SOURCE == event->type)
    {
        tp_main_loop_remove_source_2((TpSource*)event->u_event.source);
    }

    return TP_RET_OK;
}

void tp_main_loop_init(void)
{
    int i;

    g_main_loop.running = 0;
    g_main_loop.need_to_refresh = 0;

    g_main_loop.source_nr = 0;
    for(i = 0; i < TP_MAX_SOURCE; ++i)
    {
        g_main_loop.sources[i] = NULL;
    }

    g_main_loop.primary_source = tp_source_primary_create(tp_main_loop_on_event, &g_main_loop);
    tp_main_loop_add_source_2(g_main_loop.primary_source);

    return;
}

void tp_main_loop_quit(void)
{
    g_main_loop.running = 0;

	return;
}

void tp_main_loop_uninit(void)
{
	int i;

    for(i = 0; i < g_main_loop.source_nr; ++i)
    {
        tp_source_destroy(g_main_loop.sources[i]);
        g_main_loop.sources[i] = NULL;
    }
    g_main_loop.source_nr = 0;

    g_main_loop.running = 0;
    g_main_loop.need_to_refresh = 0;

	return;
}

int tp_main_loop_add_source(TpSource* source)
{
    TpEvent event;

    event.type = TP_EVENT_ADD_SOURCE;
    event.u_event.source = source;

    return tp_primary_source_queue_event(g_main_loop.primary_source, &event);
}

int tp_main_loop_remove_source(TpSource* source)
{
    TpEvent event;

    event.type = TP_EVENT_REMOVE_SOURCE;
    event.u_event.source = source;

    return tp_primary_source_queue_event(g_main_loop.primary_source, &event);
}

int tp_main_loop_run(void)
{
	TpSource* source = NULL;

	int wait_time = 0;
	int min_wait_time = 10;
	int fd = -1;
	int max_fd = 0;

	fd_set fds_read = {0};
	struct timeval tv = {0};

	int i = 0, j = 0, ret = 0;

	g_main_loop.running = 1;
	while (g_main_loop.running)
	{
        min_wait_time = 10;

		FD_ZERO(&fds_read);

		for (i = 0; i < g_main_loop.source_nr; ++i)
		{
			source = g_main_loop.sources[i];
			fd = tp_source_get(source);
			wait_time = tp_source_check(source);

			if(fd >= 0)
			{
				FD_SET(fd, &fds_read);

				if(fd > max_fd) max_fd = fd;
			}

			if(wait_time > 0 && wait_time < min_wait_time) min_wait_time = wait_time;
		}

		tv.tv_sec = min_wait_time / 1000;
		tv.tv_usec = (min_wait_time % 1000) * 1000;
		ret = select(max_fd, &fds_read, NULL, NULL, &tv);

        for(i = 0; i < g_main_loop.source_nr;)
		{
			source = g_main_loop.sources[i];
			fd = tp_source_get(source);
			wait_time = tp_source_check(source);			

			if (fd > 0 && ret >= 0 && FD_ISSET(fd, &fds_read))
			{
				if (tp_source_dispatch(source) != TP_RET_OK)
				{
					for(j = i; j < g_main_loop.source_nr; ++j)
                    {
                        g_main_loop.sources[j] = g_main_loop.sources[j+1];
                    }
                    g_main_loop.sources[g_main_loop.source_nr] = NULL;
                    g_main_loop.source_nr--;

                    tp_source_destroy(source);

					continue;
				}
			}

			if(wait_time == 0)
			{
				if(tp_source_dispatch(source) != TP_RET_OK)
				{
					for(j = i; j < g_main_loop.source_nr; ++j)
                    {
                        g_main_loop.sources[j] = g_main_loop.sources[j+1];
                    }
                    g_main_loop.sources[g_main_loop.source_nr] = NULL;
                    g_main_loop.source_nr--;

                    tp_source_destroy(source);

					continue;
				}
			}

			if (g_main_loop.need_to_refresh)
			{
				g_main_loop.need_to_refresh = 0;
				break;
			}

			++i;
		}
	}

	return TP_RET_OK;
}
